package showtime.framework.manager
{
	import flash.utils.Dictionary;
	import flash.utils.describeType;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	
	import showtime.framework.core.pattern.Singleton;
	import showtime.framework.manager.supportClasses.cache.IDataBase;
	import showtime.framework.manager.supportClasses.cache.TypeDescription;
	
	/** 
	 * @project showtime
	 * @author yinhao
	 * @date 2011-5-11
	 *
	 */
	
	public class DataBaseManager extends Singleton implements IDataBase
	{
		private var dataBase:Dictionary = null;
		
		private var typeCache:Dictionary = null;
		
		public function DataBaseManager()
		{
		
		}
		
		public static function getInstance():DataBaseManager
		{
			return DataBaseManager(Singleton.getInstanceOrCreate(DataBaseManager));
		}
		
		public function read(className:Class, id:*):*
		{
			var tableName:String = getQualifiedClassName(className);
			
			if (dataBase.hasOwnProperty(tableName))
			{
				var table:Dictionary = Dictionary(dataBase[tableName]);
				
				return table[id];
			}
			
			return null;
		}
		
		public function list(className:Class):Array
		{
			var tableName:String = getQualifiedClassName(className);
			
			if (dataBase.hasOwnProperty(tableName))
			{
				var table:Dictionary = Dictionary(dataBase[tableName]);
				
				var values:Array = [];
				
				for (var key:* in table)
				{
					values.push(table[key]);
				}
				
				return values;
				
			}
			
			return null;
		}
		
		public function writeOrUpdate(object:*):void
		{
			var tableName:String = getQualifiedClassName(object);
			
			var table:Dictionary = null;
			
			if (dataBase.hasOwnProperty(tableName))
			{
				table = dataBase[tableName];
				
				if (table.hasOwnProperty(object.id))
				{
					cascadeOperation(table[object.id], object);
				}
				else
				{
					table[object.id] = object;
				}
	
			}
			
			else
			{
				table = new Dictionary();
				
				table[object.id] = object;
				
				dataBase[tableName] = table;
			}
		
		}
	
		public function remove(object:*):void
		{			
			var tableName:String = getQualifiedClassName(object);
			
			if (dataBase.hasOwnProperty(tableName))
			{
				var table:Dictionary = dataBase[tableName];
				
				if (table.hasOwnProperty(object.id))
				{
					delete table[object.id]	;				
				}
			}
		}		
		
		override protected function initialize():void
		{
			dataBase = new Dictionary();
			
			typeCache = new Dictionary();
		}
		
		/**
		 * Description:CascadeOperation
		 * @param oldObj
		 * @param newObj
		 * 
		 */		
		private function cascadeOperation(oldObj:*, newObj:*):void
		{

			var entry:TypeDescription = null;
			
			var tableName:String = getQualifiedClassName(oldObj);
			
			if (typeCache.hasOwnProperty(tableName))
			{
				entry = typeCache[tableName];
			}
			else
			{
				var propertyNames:Array = [];
				
				
				var typeInfo:XML = describeType(oldObj is Class ? oldObj : getDefinitionByName(tableName) as Class);

				var properties:XMLList = typeInfo.factory..accessor.(@access != "writeonly") + typeInfo..variable;
	
				for each (var propertyInfo:XML in properties)
				{
					propertyNames.push(propertyInfo.@name);
				}

				entry = new TypeDescription();
				
				entry.name = tableName;
				
				entry.properties = propertyNames;
				
				entry.typeInfo = typeInfo;
				
				typeCache[tableName] = entry;
			}
			
			analyzeInstance(oldObj, newObj, entry);
		}
		
		private function analyzeInstance(oldObj:*, newObj:*, entry:TypeDescription):void
		{
			for each (var propertyKey:String in entry.properties)
			{
				if (newObj[propertyKey] == null)
				{
					continue;
				}
				oldObj[propertyKey] = newObj[propertyKey];
			}
		
		}
	}
}